/***************************************************************************************************
  *Copyright(C),2010-2016,Sumscope
  *FileName	:  StringPtrMap.cpp
  *Author	:  scofined.qi
  *Version	:  1.0
  *Date		:  2016/09/04
  *Desc		:  //用以实现 string -> void* 的哈希表
  *Relation :
  *Others	:	1.底层使用最快的哈希函数XXH32
                                2.使用list管理的对象池，用于快速调整哈希表尺寸所引发的内存分配释放
                                3.使用4kb边界的内置字符串分配器来避免大量字符串对象的释放过程
                                        内部字符串操作使用其指针，避免了字符串移动复制的开销
  *Function :
  *History	:
***************************************************************************************************/
#include "StringPtrMap.h"

#include <core/allocator/allocator_pool_manager.h>
#include <core/container/KeyHashMap.h>
#include <core/container/SHash.h>
#include <core/container/hash_prime.h>

#include <list>
#include <map>
#include <set>
namespace {
////////////////////////////////////////////////////////////////////////////////
template <class Type>
class ObjectPtrPool {
  std::list<Type*> m_unused;
  int m_capcity;  // 默认-1,表示全部缓存
  int m_allocated;
  bool m_usepool;

 public:
  ObjectPtrPool(int capcity = -1)
      : m_capcity(capcity), m_allocated(0), m_usepool(true) {}
  ~ObjectPtrPool() {
    auto it = m_unused.begin();
    for (; it != m_unused.end(); ++it) {
      delete *it;
    }
  }
  virtual void OnEntry(Type* entry, int slot) {}
  void SetUsePool(bool usePool) { m_usepool = usePool; }
  int GetCount() const { return m_unused.size(); }
  int GetCapcity() const { return m_capcity; }
  void SetCapcity(int capcity) { m_capcity = capcity; }
  Type* Alloc() {
    if (!m_usepool) return new Type();
    Type* type = 0;
    if (!m_unused.empty()) {
      type = m_unused.front();
      m_unused.pop_front();
    } else
      type = new Type();
    m_allocated++;
    return type;
  }
  void Free(Type* t) {
    if (!m_usepool) {
      delete t;
      return;
    }
    // BASSERT(t);
    m_allocated--;
    if (m_capcity > 0 && (int)m_unused.size() >= m_capcity) {
      delete t;
    } else
      m_unused.push_back(t);
  }
};
}  // namespace

class StringPtrMapImpl {
  typedef const char* StringId;
  struct Key {
    StringId id;
    const void* ptr;

   public:
    Key() : id(0), ptr(0) {}
    Key(const char* c, const void* p) : id(c), ptr(p) {}
    Key(const Key& k) : id(k.id), ptr(k.ptr) {}
    unsigned int GetHash() { return qb::base::GetFastHash(id, strlen(id), 0); }
    bool operator==(const Key& k) {
      return id == k.id || strcmp(id, k.id) == 0;
    }
  };
  struct Entry {      //
    Entry* hashprev;  // 哈西插槽的双链表的后一个
    Entry* hashnext;  // 哈西插槽的双链表的前一个
    Key key;          // 用于标志
    Entry() : hashnext(0), hashprev(0) {}
  };
  class Collector : public qb::base::KeyHashMap<Entry, Key>::Listener {
    ObjectPtrPool<Entry>* m_cache;

   public:
    Collector(ObjectPtrPool<Entry>* pcache) : m_cache(pcache) {}
    void Enum(qb::base::KeyHashMap<Entry, Key>& hashmap) { hashmap.Enum(this); }
    virtual void OnEntry(Entry* entry, int slot) { m_cache->Free(entry); }
  };

  typedef qb::base::HeapAllocator Allocator;
  Allocator* m_string_buf;                     //
  qb::base::KeyHashMap<Entry, Key> m_hashmap;  //
  ObjectPtrPool<Entry> m_cache;                //
  int m_nexpand;                               // 扩展次数
 public:
  StringPtrMapImpl() : m_nexpand(0) {
    m_string_buf = new Allocator("StringPtrMap");
    m_hashmap.Build(43853);
    m_cache.SetUsePool(false);
  }
  ~StringPtrMapImpl() {
    // BS_METRICS("");
    Collector collector(&m_cache);
    collector.Enum(m_hashmap);
    m_hashmap.Clear();
    if (m_string_buf) delete m_string_buf;
  }
  void SetAt(const char* name, void* ptr) {
    Key key(name, ptr);
    unsigned int hash = key.GetHash();
    Entry* et = m_hashmap.Find(key, hash);
    if (et == 0) {
      int len = strlen(name);
      char* buf = (char*)m_string_buf->Alloc(len + 1);
      memcpy(buf, name, len);
      buf[len] = 0;

      key.id = buf;
      et = NewEntry(key);
      m_hashmap.Insert(et, hash);
    } else {
      et->key.ptr = ptr;
    }
  }
  bool Lookup(const char* name, void*& ptr) {
    Key key(name, 0);
    unsigned int hash = key.GetHash();
    Entry* et = m_hashmap.Find(key, hash);
    if (et) ptr = (void*)et->key.ptr;
    return et != 0;
  }
  void RemoveAll() {
    // BS_METRICS("");
    Collector collector(&m_cache);
    collector.Enum(m_hashmap);
    m_hashmap.Clear();
    m_string_buf->Reset();
  }
  void Expand(int capcity) {
    m_hashmap.Expand(capcity);
    m_nexpand++;
  }
  void ExpandByRatio(double ratio) {
    int count = m_hashmap.GetCount();
    ratio = ratio < 0.4 ? 0.4 : ratio;
    ratio = ratio > 20 ? 20 : ratio;
    int length = (int)((count < 10949 ? 10949 : count) / ratio);
    m_hashmap.Expand(length);
  }
  void InitHashTable(int capcity) {
    // BS_METRICS("InitHashTable:count:%d length:%d
    // cap:%d",m_hashmap.GetCount(),m_hashmap.GetLength(),capcity);
    m_hashmap.Expand(capcity);
  }

  int GetLength() const { return m_hashmap.GetLength(); }
  double GetHashRatio() const {
    return m_hashmap.GetCount() * 1.0 / m_hashmap.GetLength();
  }
  int GetBytes() const {
    int bytes = sizeof(*this);
    bytes += m_string_buf->GetBytes();
    bytes += m_hashmap.GetLength() * sizeof(Entry*);
    bytes += m_hashmap.GetCount() * sizeof(Entry);
    bytes += m_cache.GetCount() * (sizeof(Entry*) * 3 + sizeof(Entry));
    return bytes;
  }
  void DumpStatus(int mode = 0) const {}

 protected:
  Entry* NewEntry(const Key& k) {
    Entry* et = m_cache.Alloc();
    et->hashprev = et->hashnext = 0;
    et->key = k;
    return et;
  }
};

#include <core/container/LinkedHashMap.h>

#include "BondKeyHashMap.h"
class BondKeyMapImpl {
  typedef qb::base::F14LinkedChunk<ComBondKeyCompact, BondIndex>
      BondKeyF14Cache;
  typedef qb::base::F14LinkedChunk<BondCodeCompact, BondIndex> BondCodeF14Cache;

  typedef qb::base::CacheLineArray<ComBondKeyCompact, BondIndex>
      BondKeyCacheLine;
  typedef BondKeyHashMap<ComBondKeyCompact, BondKeyParser, BondKeyF14Cache>
      FlinkedBondKeyMap;
  typedef BondKeyHashMap<ComBondKeyCompact, BondKeyCacheLine> CachedBondKeyMap;
  typedef BondKeyHashMap<BondCodeCompact, BondCodeParser, BondCodeF14Cache>
      FlinkedBondCodeMap;

  FlinkedBondKeyMap m_keymap;
  FlinkedBondCodeMap m_codemap;
  bool m_askey;

 public:
  BondKeyMapImpl(bool asKey) : m_askey(asKey) {
    /*
            [内存优化][scofined.qi]
            经过测试,该哈希表的哈希插槽数组长度为2^13,性能和内存综合最好
            因此BondKeyMapImpl中,Expand的参数为13,表示长度为2^13

            初略估计,2^13*16*2=262144
            16是一个节点能放置的最多的键值对个数,2是节点链表长度
    */
    if (asKey)
      m_keymap.Expand(13);
    else
      m_codemap.Expand(13);
  }
  ~BondKeyMapImpl() {}
  void SetAt(const char* key, void* ptr) {
    static_assert(sizeof(void*) == sizeof(intptr_t),
                  "assert failed:sizeof(void*) == sizeof(int)");
    BondIndex bid = (intptr_t)ptr;
    if (m_askey)
      m_keymap.AddBondKey(key, strlen(key), bid);
    else
      m_codemap.AddBondKey(key, strlen(key), bid);
  }
  bool RemoveKey(const char* key) {
    if (m_askey) return m_keymap.EraseBondKey(key, strlen(key));

    return m_codemap.EraseBondKey(key, strlen(key));
  }
  void RemoveAll() {
    if (m_askey)
      m_keymap.RemoveAll();
    else
      m_codemap.RemoveAll();
  }
  bool Lookup(const char* key, void*& ptr) {
    BondIndex bid = m_askey ? (m_keymap.GetBondIndex(key, strlen(key)))
                            : m_codemap.GetBondIndex(key, strlen(key));

    if (bid >= 0) ptr = (void*)bid;
    return bid >= 0;
  }
  void InitHashTable(int capcity) {
    // 不需要重新哈希
    // m_bondkeys.Expand(capcity);
  }
  int GetLength() const {
    return m_askey ? m_keymap.GetLength() : m_codemap.GetLength();
  }
  double GetHashRatio() const {
    return m_askey ? m_keymap.GetLoadRatio() : m_codemap.GetLoadRatio();
  }
  int GetBytes() const {
    return m_askey ? m_keymap.GetBytes() : m_codemap.GetBytes();
  }
  void ExpandByRatio(double ratio) {}
  void Expand(int length) {}
  void DumpStatus(int mode) const {}
};
//
typedef std::map<std::string, int> StringIntMap;
typedef std::set<int> IntSet;

// static_assert(sizeof(std::string) == 24, "sizeof(std::string) == 24");
// static_assert(sizeof(StringIntMap::value_type) == 28, "sizeof(std::string) ==
// 24"); static_assert(sizeof(StringIntMap::mapped_type) == 4,
// "sizeof(std::string) == 24"); static_assert(sizeof(StringIntMap::_Node) ==
// 44, "sizeof(std::string) == 24"); 键
////////////////////////////////////////////////////////////////////////////////
StringPtrMap::StringPtrMap(kKeyMode mode) : m_mode(mode), m_impl(0) {
  m_impl = new KeyMapImpl(mode == kKeyModeAsKey);
}

StringPtrMap::~StringPtrMap() {
  if (m_impl) {
    delete m_impl;
    m_impl = 0;
  }
}

void StringPtrMap::SetAt(const char* key, void* ptr) {
  m_impl->SetAt(key, ptr);
}

bool StringPtrMap::Lookup(const char* key, void*& ptr) {
  return m_impl->Lookup(key, ptr);
}
bool StringPtrMap::RemoveKey(const char* key) { return m_impl->RemoveKey(key); }
void StringPtrMap::RemoveAll() {
  // BS_METRICS("RemoveAll");
  m_impl->RemoveAll();
}

void StringPtrMap::InitHashTable(int capcity) {
  // BS_METRICS("InitHashTable:%d",capcity);
  m_impl->InitHashTable(capcity);
}

void StringPtrMap::DumpStatus(int mode) const {}
////////////////////////////////////////////////////////////////////////////////
double StringPtrMap::GetHashRatio() const { return m_impl->GetHashRatio(); }
int StringPtrMap::GetLength() const { return m_impl->GetLength(); }
int StringPtrMap::GetBytes() const { return m_impl->GetBytes(); }
void StringPtrMap::Expand(int length) { m_impl->Expand(length); }
void StringPtrMap::ExpandByRatio(double ratio) { m_impl->ExpandByRatio(ratio); }
